/*********************************************************************************
 *      Copyright:  (C) 2019 Wu Yujun<540726307@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  mosq_sub.c
 *    Description:  This file mosquitto demo sub
 *                 
 *        Version:  1.0.0(2019年07月22日)
 *         Author:  Wu Yujun <540726307@qq.com>
 *      ChangeLog:  1, Release initial version on "2019年07月22日 19时39分59秒"
 *                 
 ********************************************************************************/


#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <libgen.h>
#include <getopt.h>

#include "mosquitto.h"
#include "cJSON.h"
#include "mosq_conf.h"
#include "iniparser.h"


#define MSG_MAX_SIZE    512
#define INI_PATH "./mqtt_ali_conf.ini"



void find_identi_Json(cJSON * root,const char* identifier, char *reval,int reval_size) ;//以递归的方式寻找对应的标识符对应的值
void printJson(cJSON * root) ;//以递归的方式打印json的最内层键值对
void message_callback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message) ;
void conn_callback(struct mosquitto *mosq, void *obj, int rc) ;


typedef struct _st_mosq_ctx
{
    char    identifier[64] ;    //value identifier in jSON
    char    topic[256] ;        
    int     pubQos ;
    char    method[256] ; 
    char    json_id[256] ;
    char    version[32] ;

}st_mosq_ctx ;


void printJson(cJSON * root)//以递归的方式打印json的最内层键值对
{
    int             i ;
    for(i=0; i<cJSON_GetArraySize(root); i++)   //遍历最外层json键值对
    {
        cJSON * item = cJSON_GetArrayItem(root, i);        
        if(cJSON_Object == item->type)      //如果对应键的值仍为cJSON_Object就递归调用printJson
            printJson(item);
        else                                //值不为json对象就直接打印出键和值
        {
            printf("%s->", item->string);
            printf("%s\n", cJSON_Print(item));
        }
    }
}

void find_identi_Json(cJSON * root,const char* identifier, char *reval,int reval_size) //以递归的方式寻找对应的标识符对应的值
{
    int             i ;
    for(i=0; i<cJSON_GetArraySize(root); i++)   //遍历最外层json键值对
    {
        cJSON * item = cJSON_GetArrayItem(root, i);        
        if(cJSON_Object == item->type)      //如果对应键的值仍为cJSON_Object就递归查找
            find_identi_Json(item, identifier, reval ,reval_size);
        else                                
        {
            if(!strcasecmp(item->string, identifier))
                strncpy(reval,cJSON_Print(item), reval_size);
        }
    }
}

void message_callback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message)
{
    st_mosq_ctx         *mosq_ctx ;
    cJSON               *payload_json =  cJSON_CreateObject();
    char                value[256] ;
    
    if(!userdata)
    {
        printf("Invail parameter in %s\n",__FUNCTION__) ;
        return ;
    }

    mosq_ctx = (st_mosq_ctx*)userdata ;
    if(message->payloadlen)
    {
        payload_json = cJSON_Parse(message->payload); 

#if 0 
        printJson(payload_json) ;
#endif
        printf("%s\n",cJSON_Print(payload_json));
        find_identi_Json(payload_json,mosq_ctx->identifier, value, sizeof(value) ) ;
        printf("\n\"%s\":\"%s\"\n", mosq_ctx->identifier, value);


    }
}

void conn_callback(struct mosquitto *mosq, void *obj, int rc)
{
    int                 mid ;
    st_mosq_ctx         *mosq_ctx ;

    if(!obj)
    {
        printf("Invail parameter in %s\n", __FUNCTION__) ;
        return ;
    }
    mosq_ctx = (st_mosq_ctx *)obj ;
    if(!rc)
    {
        printf("connect server sucessful!\n") ;
        if( mosquitto_subscribe(mosq, &mid, mosq_ctx->topic, 0) != MOSQ_ERR_SUCCESS)
        {
            printf("mosquitto_subscribe() error:%s\n",strerror(errno)) ;
        }    
        printf("subscribe topic:%s\n",mosq_ctx->topic) ;
    }

}

void print_usage(const char *program_name)
{
    printf("%s -- (2019.7.23)\n", program_name);
    printf(" Usage:%s [-h <host_name>] [-p <server_port>] [-i<client_id>] [-u <user>] [-P <passwd>] [-t <topic>] [-H <use_help>]\n", program_name);
    printf("        -p --port       the port of the server you want to connect\n") ;
    printf("        -h --host       the hostname of the server you want to connect\n") ;
    printf("        -u --user       the username of the client\n") ;
    printf("        -P --passwd     the passwd of the client you\n") ;
    printf("        -t --topic      the topic of the client you want to sub\n") ;
    printf("        -H --help       the client file how to use\n");
    printf("        -d --daemon     the client progame running in backgruand\n");

    return ;
}

int main(int argc, char **argv)
{
    int                 rv = -1 ;
    st_mqtt             mqtt ;
    struct mosquitto    *mosq = NULL;
    bool                session = true ;
    st_mosq_ctx         mosq_ctx ;

    int                 opt = -1 ;
    int                 daemon_run = 0 ;
    char                *program_name ;
    char                *host=NULL ;
    int                 port = 0 ;
    char                *id = NULL ;
    char                *user = NULL ;
    char                *passwd = NULL ;
    char                *topic = NULL ;
    
    program_name = basename(argv[0]) ;

    const char *short_opts = "i:h:p:P:u:t:dH";                    
    const struct option long_opts[] =   {  
        {"host", required_argument, NULL, 'h'},    
        {"daemon", no_argument, NULL, 'd'},  
        {"help", no_argument, NULL, 'H'},  
        {"port", required_argument, NULL, 'p'},  
        {"id", required_argument, NULL, 'i'},  
        {"passwd", required_argument, NULL, 'P'},  
        {"user", required_argument, NULL, 'u'},  
        {"topic", required_argument, NULL, 't'},  
        {0, 0, 0, 0} 
    };  
    while ((opt= getopt_long(argc, argv, short_opts, long_opts,NULL)) != -1) 
    {
        switch (opt) 
        {
            case 'i':
                id = optarg ;
                break ;
            case 'P':
                passwd = optarg ;
                break ;
            case 'u':
                user = optarg ;
                break ;
            case 'p':
                port = atoi(optarg) ;
                break ;
            case 'd':
                daemon_run = 1 ;
                break ;
            case 'h':
                host = optarg ;
                break ;
            case 't':
                topic  = optarg ;
                break ;
            case 'H':
                print_usage(program_name) ;
                return 0 ;
        }
    }
    set_mqtt_conf(INI_PATH,host, port, id, user, passwd, topic) ;

    memset(&mosq_ctx,0,sizeof(mosq_ctx)) ;
    memset(&mqtt,0,sizeof(mqtt)) ;

    rv = gain_mqtt_conf(INI_PATH,&mqtt,TYPE_PUB) ;
    if(rv < 0)
    {
        printf("gain mqtt conf error\n") ;
        return -1 ;
    }
    strncpy( mosq_ctx.topic, mqtt.topic, sizeof(mosq_ctx.topic)) ;
    strncpy( mosq_ctx.identifier, mqtt.identifier, sizeof(mosq_ctx.identifier)) ;

    /*  Program running in backgrund    */
    if(daemon_run)
    {
        daemon(1,1) ;
    }

    /*   必须在任何其他mosquitto功能之前调用。 */
    mosquitto_lib_init() ;

    /*   创建一个新的mosquitto客户端实例 */
    mosq = mosquitto_new(mqtt.clientid, session, (void*)&mosq_ctx) ;
    if(!mosq)
    {
        printf("Mosquitto_new() failed: %s\n", strerror(errno)) ;
        goto cleanup ;
    }
    printf("Create mosquitto sucessfully!\n");


    /*      Set callback function    */
    mosquitto_connect_callback_set(mosq, conn_callback) ;
    mosquitto_message_callback_set(mosq, message_callback);



    /*   配置mosquitton实例的用户名和密码    */
    if( mosquitto_username_pw_set(mosq, mqtt.username, mqtt.passwd) != MOSQ_ERR_SUCCESS ) 
    {
        printf("Mosq_usee_pw_set() failed: %s\n", strerror(errno) );
        goto cleanup ;
    }

    if( mosquitto_connect(mosq, mqtt.hostname, mqtt.port, KEEP_ALIVE) )
    {
        printf("connect mqtt server failed\n") ;
        goto cleanup ;
    }
    mosquitto_loop_forever(mosq, -1, 1);


cleanup:
    printf("program will exit\n");
    mosquitto_destroy(mosq) ;
    mosquitto_lib_cleanup() ;

    return 0 ;
}
